package demo.java.util;

import com.alibaba.fastjson.JSONObject;
import demo.vo.ProvinceCity;
import org.junit.Test;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * @author hanjy
 */
public class SetDemo {

    /**
     * 基于ConcurrentSkipListMap的有序Set实现，适合需要排序的并发场景。
     */
    @Test
    public void testConcurrentSkipListSet(){
        Set<String> set = new ConcurrentSkipListSet<>();
    }


    /**
     * 高并发性能，ConcurrentHashMap.newKeySet()是个好选择。
     */
    @Test
    public void testConcurrentHashSet() {
        Set<Integer> set = ConcurrentHashMap.newKeySet();
        IntStream.range(0, 10).forEach(set::add);
        System.out.println(set);

        set.removeIf(i -> i % 2 == 0);
        System.out.println(set);
    }

    @Test
    public void testToString() {
        Set<String> set = new HashSet<String>();
        set.add("han");
        set.add("jun");
        set.add("ying");
        System.out.println(set.toString());
        System.out.println(set.contains(null));
    }

    /**
     * 通过Iterator remove 元素
     */
    @Test
    public void remove() {
        Set<String> set = new HashSet<String>();
        set.add("han");
        set.add("jun");
        set.add("ying");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String string = iterator.next();
            System.out.println(string);
            iterator.remove();
        }
        System.out.println("set:"+ set);
    }

    /**
     * 线程安全的,如果你的场景是读多写少，可以考虑CopyOnWriteArraySet。
     */
    @Test
    public void testCopyOnWriteArraySet() {
        CopyOnWriteArraySet<String> tt = new CopyOnWriteArraySet<>();
        Set<String> set = new CopyOnWriteArraySet<>();
        ExecutorService service = Executors.newFixedThreadPool(12);
        int times = 10000;
        AtomicInteger flag = new AtomicInteger(0);
        for (int i = 0; i < times; i++) {
            service.execute(() -> {
                set.add("a" + flag.getAndAdd(1));
            });
        }
        service.shutdown();
        try {
            service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(set.size());
    }

    /**
     * 基本的线程安全性
     */
    @Test
    public void synchronizedSet() {
        Set<String> synSet = Collections.synchronizedSet(new HashSet<>());
        ExecutorService service = Executors.newFixedThreadPool(12);
        int times = 10000;
        AtomicInteger flag = new AtomicInteger(0);
        for (int i = 0; i < times; i++) {
            service.execute(() -> {
                synSet.add("a" + flag.getAndAdd(1));
            });
        }
        service.shutdown();
        try {
            service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(synSet.size());
    }

    /**
     * TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一。 TreeSet是用来排序的, 可以指定一个顺序,
     * 对象存入之后会按照指定的顺序排列
     * <p>
     * 使用方式
     * <li>a.自然顺序(Comparable)
     * <li>b.比较器顺序(Comparator)
     * <li>c.两种方式的区别： TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)
     * TreeSet如果传入Comparator, 就优先按照Comparator
     */
    @Test
    public void testTreeSet() {
        TreeSet<String> treeSet = new TreeSet<String>();
        treeSet.add("han");
        treeSet.add("jun");
        treeSet.add("ying");
        treeSet.add("Stan");
        treeSet.add("abc");

        System.out.println( treeSet);
        Iterator<String> iterator = treeSet.iterator();
        while (iterator.hasNext()) {
            String string = iterator.next();
            System.out.println(string);
        }
        /*
         * Object[] array = treeSet.toArray(); for (Object string : array) {
         * System.out.println(string); }
         */
    }

    /**
     * 交集
     *
     * @param c1
     * @param c2
     * @return
     */
    public static <T> Set<T> processIntersection(Collection<T> c1, Collection<T> c2) {
        Set<T> intersection = new HashSet<>();

        if (c1 == null || c1.isEmpty() || c2 == null || c2.isEmpty()) {
            return intersection;
        }
        intersection.addAll(c1);
        intersection.retainAll(c2);
        return intersection;
    }

    /**
     * set1 减去 set2 的差集
     *
     * @param set1
     * @param set2
     * @return
     */
    public static Set<String> differenceSet(Set<String> set1, Set<String> set2) {
        Set<String> diff = new HashSet<String>();
        if (set1 == null || set1.isEmpty()) {
            return diff;
        }
        if (set2 == null || set2.isEmpty()) {
            return set1;
        }
        diff.addAll(set1);
        diff.removeAll(set2);
        return diff;
    }

    /**
     * 测试集合交集、并集、差集等操作
     */
    @Test
    public void testSet() {
        Set<Integer> result = new HashSet<Integer>();

        System.out.println("set1=" + set1);
        System.out.println("set2=" + set2);
        System.out.println("emptySet=" + emptySet);
        System.out.println("nullSet=" + nullSet);

        result.clear();
        System.out.println(result.addAll(set1));
        System.out.println(result.retainAll(set2));
        System.out.println("set1 与 set2 交集：" + result);

        result.clear();
        System.out.println(result.addAll(set1));
        System.out.println(result.removeAll(set2));
        System.out.println("set1 与 set2 差集：" + result);

        result.clear();
        System.out.println(result.addAll(set1));
        System.out.println(result.addAll(set2));
        System.out.println("set1 与 set2 并集：" + result);

        System.out.println(set1.removeAll(emptySet));
        System.out.println("set1=" + set1);
        System.out.println("emptySet=" + emptySet);

        result.clear();
        System.out.println(result.addAll(set1));
        System.out.println(result.addAll(nullSet));
        System.out.println("set1 与 nullSet 并集：" + result);
    }

    /**
     * 求SET
     *
     * @param collections
     * @return
     */
    public static Set<String> addAll(Collection<String>... collections) {
        Set<String> set = new HashSet<String>();
        if (collections == null || collections.length == 0) {
            return set;
        }
        for (Collection<String> collection : collections) {
            if (collection != null && !collection.isEmpty()) {
                set.addAll(collection);
            }
        }
        return set;
    }

    /**
     * 测试集合交集、并集、差集等操作
     */
    @Test
    public void testIntersection交集() {
        Set<Integer> result = new HashSet<Integer>();
        System.out.println("set1=" + set1);
        System.out.println("set2=" + set2);
        System.out.println(result.addAll(set1));
        System.out.println(result.retainAll(set2));
        System.out.println("set1 与 set2 交集：" + result);
        System.err.println(processIntersection(set1, set2));
    }

    /**
     * <li>FOR EACH 和Iterator 遍历出来的顺序一样
     * <li>HASHSET允许null值
     */
    @Test
    public void testHashSet() {
        HashSet<String> set = new HashSet<>();
        set.add(null);
        set.add("b20");
        set.add("c3");
        set.add("D4");
        set.add("e5");
        set.add("a1");
        set.add("");
        System.out.println(set.size());

        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " , ");
        }
        System.out.println(set.toString());
        for (String string : set) {
            System.out.print(string + " , ");
        }
    }

    /**
     * <li>第二次添加相同的对象，返回FALSE，且不替换
     */
    @Test
    public void testHashSetAdd() {
        HashSet<ProvinceCity> city = new HashSet<>();
        ProvinceCity c1 = new ProvinceCity("上海", "上海");
        c1.setUpdateTime(LocalDateTime.now());
        boolean r = city.add(c1);
        System.out.println(r);
        System.out.println(JSONObject.toJSONString(city));
        ProvinceCity c2 = new ProvinceCity("上海", "上海");
        c2.setUpdateTime(LocalDateTime.now().minusDays(1));
        r = city.add(c2);
        System.out.println(r);
        System.out.println(JSONObject.toJSONString(city));
    }

    static Set<Integer> set1 = new HashSet<Integer>() {
        {
            add(1);
            add(2);
        }
    };

    static Set<Integer> set2 = new HashSet<Integer>() {
        {
            add(1);
            add(3);
        }
    };

    static Set<Integer> emptySet = new HashSet<Integer>();
    static Set<Integer> nullSet = null;
}
